#include "list.h"

#include <malloc.h>
#include <string.h>
#include <assert.h>
C_CODE_BEGIN
/*
 * list is a littel complicated data structure ,I use a double link list to implement
 */
 
 struct list_node {
	 struct list_node* prev;
	 struct list_node* next;
 };

 struct list_cache {
	 struct list_cache* next;
 };
 
 struct list {
	 size_t objsize;
	 size_t usize;
	 struct list_node* tail;
	 // for cache 
	 size_t csize;
	 struct list_cache* entry;
 };
 
 static struct list_node* _next_cache_entry ( struct list* li ) {
	struct list_cache* ne = li->entry;
	 if ( ne == NULL ) {
		 return NULL;
	 }
	 li->entry = ne->next;
	 return (struct list_node*)ne;
 }
 
 static void _back_cache_entry ( struct list* li , struct list_node* node ) {
	 struct list_cache* c = (struct list_cache*)( (char*)(node));
	 if( li->entry == NULL ) {
		 li->entry = c;
		 c->next = NULL;
	 } else {
		 c->next = li->entry;
		 li->entry = c;
	 }
 }
 
 static struct list_cache* _build_cache_entry( char* buf , size_t csize , size_t objsize , struct list* li) {
	 struct list_cache* en = (struct list_cache*)buf;
	 size_t i = 1 ;
	 for( ; i < csize ; ++i ) {
		 en->next = (struct list_cache*)( (char*)(en)+objsize+sizeof(struct list_node) );
		 en = en->next;
	 }
	 en->next = NULL;
	 return (struct list_cache*)buf;
 }

 
EXPORT_FUNC struct list* list_create( size_t objsize , size_t cache_size) {
 /*
  * This malloc operation is a little trick , it will allocate a simple local cache
  * for memory operation.Which is cache_size * objsize 
  */
  char* rawmem = malloc( sizeof(struct list) + (objsize+sizeof(struct list_node))*cache_size + sizeof(struct list_node) );
  struct list* stub = (struct list*)rawmem;
  stub->tail = ( struct list_node*) ( rawmem + sizeof(struct list) );
  stub->tail->next = stub->tail;
  stub->tail->prev = stub->tail;
  stub->csize = cache_size;
  stub->objsize = objsize;
  stub->usize = 0;
  stub->entry = _build_cache_entry( rawmem+sizeof(struct list)+sizeof(struct list_node) , cache_size , objsize , stub);
  return stub;
}

EXPORT_FUNC void list_destroy( struct list* li ) {
	// free the list 
	list_clear(li);
	free(li);
}

static bool _is_in_cache( struct list* li , struct list_node* no ) {
	 size_t addr = (size_t)(no);
	 size_t lower = (size_t)( (char*)(li) + sizeof(struct list) + sizeof(struct list_node)) ;
	 size_t upper = lower + li->csize * (li->objsize+sizeof(struct list_node) );
	 return (addr >= lower && addr < upper);
}
 
static struct list_node* _malloc_list_node( struct list* li ) {
	 // check the cache is availabel or not
	 void* ret = _next_cache_entry(li);
	 if ( ret == NULL ) {
		 ret = malloc( sizeof(struct list_node) + li->objsize );
	 }
	 return ret;
}
 
static void _free_list_node( struct list* li , struct list_node* n ) {
	 if( _is_in_cache(li,n) ) {
		 _back_cache_entry(li,n);
	 } else {
		 free(n);
	 }
}
 

static void _list_insert( struct list* li , void* obj , struct list_node* no ) {
	struct list_node* nn = _malloc_list_node(li);
	memcpy( (char*)nn+sizeof(struct list_node), obj , li->objsize );
	nn->next = no;
	no->prev->next = nn;
	nn->prev = no->prev;
	no->prev = nn;
	++(li->usize);
}

EXPORT_FUNC void list_insert( struct list* li , void* obj , void* where ) {
	_list_insert( li , obj , (struct list_node*)( (char*)(where) - sizeof(struct list_node) ) );
}

static void _list_erase( struct list* li , struct list_node* no ) {
	struct list_node* p ;
	assert( no != li->tail );
	p = no->prev;
	p->next = no->next;
	no->next->prev = p;
	// now we give back this piece of node memory
	_free_list_node(li,no);
	--(li->usize);
}

EXPORT_FUNC void  list_erase( struct list* li , void* where ) {
	_list_erase( li , (struct list_node*)( (char*)(where) - sizeof(struct list_node)) );
}

EXPORT_FUNC void list_push_front( struct list* li , void* what ) {
	_list_insert( li , what , li->tail->next );
}

EXPORT_FUNC void list_push_back( struct list* li , void* what ) {
	_list_insert( li , what, li->tail );
}

EXPORT_FUNC void list_pop_front(struct list* li ) {
	_list_erase( li , li->tail->next );
}

EXPORT_FUNC void list_pop_back( struct list* li ) {
	_list_erase(li,li->tail->prev);
}

EXPORT_FUNC void* list_front( struct list* li ) {
	return li->tail == li->tail->next ? NULL : (char*)(li->tail->next)+sizeof(struct list_node);
}

EXPORT_FUNC void* list_back( struct list* li ) {
	return li->tail == li->tail->prev ? NULL : (char*)(li->tail->prev)+sizeof(struct list_node);
}

EXPORT_FUNC void* list_head( struct list* li ) {
	return (char*)(li->tail->next)+sizeof(struct list_node);
}

EXPORT_FUNC void* list_tail( struct list* li ) {
	return (char*)(li->tail)+sizeof(struct list_node);
}

EXPORT_FUNC void* list_move_list_node( int direction , void* no , struct list* li ) {
	struct list_node* node = (struct list_node*) ( (char*)no - sizeof(struct list_node) );
	assert( node!=li->tail );
	if( direction == 0 )
		return no;
	else if( direction > 0 ) {
		return (char*)(node->next)+sizeof(struct list_node);
	} else {
		return (char*)(node->prev)+sizeof(struct list_node);
	}
}


EXPORT_FUNC void list_until( struct list* li, bool (*func)(void* node,void* others), void* others ) {
	void* t = list_head(li);
	void* e = list_tail(li);
	while( t != e ) {
		if( func(t,others) == false )
			return;
		t = list_move_list_node(1,t,li);
	}
}


struct fewraper {
	void (*func)(void* node,void* others);
	void* par;
};


static INLINE bool _foreach_wrapper( void* node , void* others) {
	struct fewraper * fe = ( struct fewraper * )(others);
	fe->func( node , fe->par );
	return true;
}

EXPORT_FUNC void list_foreach( struct list* li , void(*func)( void* node ,void* others) ,void* others) {
	struct fewraper fe;
	fe.func = func;
	fe.par = others;
	list_until(  li , _foreach_wrapper , &fe );
}

static void _list_clear(  void* node , void* others ) {
	list_erase( (struct list*)(others) , node );
}
EXPORT_FUNC void list_clear( struct list* li ) {
	list_foreach( li , _list_clear , li );
}

EXPORT_FUNC size_t list_get_usesize( struct list* li ) {
	return li->usize;
}

EXPORT_FUNC size_t list_get_objsize( struct list* li ) {
	return li->objsize;
}
  
C_CODE_END

